SPB Git forge

spb/websensor

Public
33commits 1branches 0releases
3.4 MBsize
maindefault branch
10 days agolast push
TypeScript 55.4% Python 43.2% SQL 1.2%
4.7 KB · 91 lines tsx
Raw Blame History
1import { ImageResponse } from "next/og";2import { Mark } from "@/components/brand";3import { api } from "@/lib/api";4import { fmtScore, utcDateTime } from "@/lib/format";56export const runtime = "nodejs";7export const alt = "WebSensor event";8export const size = { width: 1200, height: 630 };9export const contentType = "image/png";1011const BG = "#0a0e15";12const PANEL = "#10161f";13const LINE = "#2d394e";14const FG = "#e6e8ee";15const MUTED = "#a3adc2";16const SUBTLE = "#6b7591";17const SIGNAL = "#22d3a5";18const HOT = "#ff5c5c";19const HIGH = "#ff9640";20const MID = "#e4b53b";21const SILENT = "#a78bfa";2223function scoreColor(v: number): string {24  return v >= 90 ? HOT : v >= 75 ? HIGH : v >= 50 ? MID : SUBTLE;25}2627function clip(s: string, max: number): string {28  return s.length > max ? s.slice(0, max - 1).trimEnd() + "…" : s;29}3031export default async function OpenGraphImage({ params }: { params: Promise<{ slug: string }> }) {32  const { slug } = await params;33  const d = await api.event(slug);34  const e = d?.event ?? null;35  const signal = e ? Math.round(e.signal_score ?? e.importance) : 0;36  const badges: { label: string; color: string }[] = [];37  if (e?.cluster?.state === "breaking") badges.push({ label: "BREAKING", color: HOT });38  if (e?.cluster?.state === "developing") badges.push({ label: "DEVELOPING", color: HIGH });39  if (e?.silent_change) badges.push({ label: "SILENT", color: SILENT });40  if (e && e.first_party !== false) badges.push({ label: "FIRST PARTY", color: SIGNAL });41  if (e?.first_party === false) badges.push({ label: "EXTERNAL", color: SUBTLE });42  if (e?.evidence_label === "CONFIRMED") badges.push({ label: "CONFIRMED", color: SIGNAL });43  const title = e ? clip(e.title, 150) : "Event not found";44  const fontSize = title.length > 110 ? 40 : title.length > 70 ? 46 : 54;4546  return new ImageResponse(47    (48      <div style={{ width: "100%", height: "100%", display: "flex", flexDirection: "column", justifyContent: "space-between", padding: 56, background: BG, color: FG, fontFamily: "Inter, system-ui, sans-serif" }}>49        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>50          <div style={{ display: "flex", alignItems: "center", gap: 14, fontSize: 30, fontWeight: 700 }}>51            <Mark size={44} />52            <span>53              Web<span style={{ color: SIGNAL }}>Sensor</span>54            </span>55          </div>56          <div style={{ display: "flex", fontSize: 22, color: SUBTLE, fontFamily: "monospace", letterSpacing: 2 }}>{e ? (e.event_type ?? "").replace(/_/g, " ").toUpperCase() : ""}</div>57        </div>5859        <div style={{ display: "flex", flexDirection: "column", gap: 20, flex: 1, justifyContent: "center", paddingTop: 24, paddingBottom: 24 }}>60          <div style={{ display: "flex", alignItems: "center", gap: 14, fontFamily: "monospace", fontSize: 26, color: MUTED, letterSpacing: 3, fontWeight: 700 }}>61            <span>{(e?.source?.name ?? "").toUpperCase()}</span>62            {e?.country ? <span style={{ color: SUBTLE, fontSize: 22 }}>· {e.country}</span> : null}63          </div>64          <div style={{ display: "flex", flexDirection: "row", gap: 32, alignItems: "flex-start" }}>65            <div style={{ display: "flex", flex: 1, fontSize, fontWeight: 700, letterSpacing: -1.2, lineHeight: 1.12, color: FG, overflow: "hidden", maxHeight: fontSize * 1.12 * 3 + 6 }}>{title}</div>66            <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", width: 200, height: 160, borderRadius: 12, border: `2px solid ${scoreColor(signal)}`, background: PANEL, flexShrink: 0 }}>67              <div style={{ display: "flex", fontFamily: "monospace", fontSize: 88, fontWeight: 700, color: scoreColor(signal), lineHeight: 1 }}>{e ? fmtScore(signal) : "—"}</div>68              <div style={{ display: "flex", fontSize: 16, letterSpacing: 3, color: SUBTLE, marginTop: 10, fontWeight: 600 }}>SIGNAL</div>69            </div>70          </div>71          {badges.length > 0 && (72            <div style={{ display: "flex", gap: 10 }}>73              {badges.map((b) => (74                <div key={b.label} style={{ display: "flex", padding: "6px 12px", borderRadius: 6, border: `1.5px solid ${b.color}`, color: b.color, fontFamily: "monospace", fontSize: 20, fontWeight: 700, letterSpacing: 2 }}>75                  {b.label}76                </div>77              ))}78            </div>79          )}80        </div>8182        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", borderTop: `1px solid ${LINE}`, paddingTop: 20, fontSize: 22, color: SUBTLE, fontFamily: "monospace" }}>83          <span>www.websensor.io</span>84          <span>{e ? `DETECTED ${utcDateTime(e.detected_at)}` : "LIVE · UTC"}</span>85        </div>86      </div>87    ),88    { ...size },89  );90}91